macro_rules! magic_number {
    ($name:ident, $value:expr) => {
        const $name: u32 = $value;
    };
}
magic_number!(ALLOC_TEST_MAGIC, 0x72ec82d2);
magic_number!(MALLOC_PATTERN, 0xBAADF00D);
magic_number!(FREE_PATTERN, 0xDEADBEEF);
use crate::src::struct_and_type::{size_t, BlockHeader};

extern "C" {
    fn malloc(_: u64) -> *mut ();
    fn free(_: *mut ());
    fn memcpy(_: *mut (), _: *const (), _: u64) -> *mut ();
    fn memset(_: *mut (), _: i32, _: u64) -> *mut ();
    fn strcpy(_: *mut i8, _: *const i8) -> *mut i8;
    fn strlen(_: *const i8) -> u64;
}
static mut allocated_bytes: size_t = 0 as size_t;
#[no_mangle]
pub static mut allocation_limit: i32 = -(1 as i32);
extern "C" fn alloc_test_get_header(mut ptr: *mut ()) -> *mut BlockHeader {
    let mut result: *mut BlockHeader = 0 as *mut BlockHeader;
    result = unsafe { (ptr as *mut BlockHeader).offset(-(1 as i32 as isize)) };
    assert!((unsafe { (*result).magic_number }) == ALLOC_TEST_MAGIC as i32 as u32);
    return result;
}
extern "C" fn alloc_test_overwrite(
    mut ptr: *mut (),
    mut length: size_t,
    mut pattern: u32,
) {
    let mut byte_ptr: *mut u8 = 0 as *mut u8;
    let mut pattern_seq: i32 = 0;
    let mut b: u8 = 0;
    let mut i: size_t = 0;
    byte_ptr = ptr as *mut u8;
    i = 0 as size_t;
    while i < length {
        pattern_seq = (i & 3 as u64) as i32;
        b = (pattern >> 8 * pattern_seq & 0xff as i32 as u32) as u8;
        (unsafe { *byte_ptr.offset(i as isize) = b });
        i = i.wrapping_add(1);
        i;
    }
}
#[no_mangle]
pub extern "C" fn alloc_test_malloc(mut bytes: size_t) -> *mut () {
    let mut header: *mut BlockHeader = 0 as *mut BlockHeader;
    let mut ptr: *mut () = core::ptr::null_mut();
    if (unsafe { allocation_limit }) == 0 {
        return core::ptr::null_mut();
    }
    header = (unsafe { malloc((::core::mem::size_of::<BlockHeader>() as u64).wrapping_add(bytes)) })
        as *mut BlockHeader;
    if header.is_null() {
        return core::ptr::null_mut();
    }
    (unsafe { (*header).magic_number = ALLOC_TEST_MAGIC as i32 as u32 });
    (unsafe { (*header).bytes = bytes });
    ptr = (unsafe { header.offset(1 as i32 as isize) }) as *mut ();
    alloc_test_overwrite(ptr, bytes, MALLOC_PATTERN as u32);
    (unsafe { allocated_bytes = (allocated_bytes as u64).wrapping_add(bytes) as size_t as size_t });
    if (unsafe { allocation_limit }) > 0 {
        (unsafe { allocation_limit -= 1 });
        (unsafe { allocation_limit });
    }
    return (unsafe { header.offset(1 as i32 as isize) }) as *mut ();
}
#[no_mangle]
pub extern "C" fn alloc_test_free(mut ptr: *mut ()) {
    let mut header: *mut BlockHeader = 0 as *mut BlockHeader;
    let mut block_size: size_t = 0;
    if ptr.is_null() {
        return;
    }
    header = alloc_test_get_header(ptr);
    block_size = unsafe { (*header).bytes };
    assert!((unsafe { allocated_bytes }) >= block_size);
    alloc_test_overwrite(ptr, unsafe { (*header).bytes }, FREE_PATTERN as u32);
    (unsafe { (*header).magic_number = 0 as u32 });
    (unsafe { free(header as *mut ()) });
    (unsafe {
        allocated_bytes = (allocated_bytes as u64).wrapping_sub(block_size) as size_t as size_t
    });
}
#[no_mangle]
pub extern "C" fn alloc_test_realloc(
    mut ptr: *mut (),
    mut bytes: size_t,
) -> *mut () {
    let mut header: *mut BlockHeader = 0 as *mut BlockHeader;
    let mut new_ptr: *mut () = core::ptr::null_mut();
    let mut bytes_to_copy: size_t = 0;
    new_ptr = alloc_test_malloc(bytes);
    if new_ptr.is_null() {
        return core::ptr::null_mut();
    }
    if !ptr.is_null() {
        header = alloc_test_get_header(ptr);
        bytes_to_copy = unsafe { (*header).bytes };
        if bytes_to_copy > bytes {
            bytes_to_copy = bytes;
        }
        (unsafe { memcpy(new_ptr, ptr, bytes_to_copy) });
        alloc_test_free(ptr);
    }
    return new_ptr;
}
#[no_mangle]
pub extern "C" fn alloc_test_calloc(mut nmemb: size_t, mut bytes: size_t) -> *mut () {
    let mut result: *mut () = core::ptr::null_mut();
    let mut total_bytes: size_t = nmemb.wrapping_mul(bytes);
    result = alloc_test_malloc(total_bytes);
    if result.is_null() {
        return core::ptr::null_mut();
    }
    (unsafe { memset(result, 0, total_bytes) });
    return result;
}
#[no_mangle]
pub extern "C" fn alloc_test_strdup(mut string: *const i8) -> *mut i8 {
    let mut result: *mut i8 = 0 as *mut i8;
    result =
        alloc_test_malloc((unsafe { strlen(string) }).wrapping_add(1 as i32 as u64)) as *mut i8;
    if result.is_null() {
        return 0 as *mut i8;
    }
    (unsafe { strcpy(result, string) });
    return result;
}
#[no_mangle]
pub extern "C" fn alloc_test_set_limit(mut alloc_count: i32) {
    (unsafe { allocation_limit = alloc_count });
}
#[no_mangle]
pub extern "C" fn alloc_test_get_allocated() -> size_t {
    return unsafe { allocated_bytes };
}
